/*
 *  AdvancedMouseEventsMonitor.cs
 *  
 *  Monitors advanced mouse events on specific control.
 *  
 *  Copyright (c) 2009, Richard Bao Labs.
 *  
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.1 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  
 *  http://www.mozilla.org/MPL/
 *  
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 *  the License for the specific language governing rights and limitations
 *  under the License.
 *  
 *  The Original Code is OpenPainter.
 *  
 *  The Initial Developer of the Original Code is OpenPainter.org.
 *  All Rights Reserved.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace RichControlLibrary
{
    /// <summary>
    /// Monitors advanced mouse events on specific control.
    /// </summary>
    public class AdvancedMouseEventsMonitor : IDisposable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AdvancedMouseEventsMonitor"/> with the specified 
        /// <see cref="System.Windows.Forms.Control"/>.
        /// </summary>
        /// <param name="control">A <see cref="System.Windows.Forms.Control"/> used for advanced mouse 
        /// events listening.</param>
        public AdvancedMouseEventsMonitor(Control control)
        {
            Register(control);
        }

        private bool _isDragging = false;
        /// <summary>
        /// Gets a boolean value that indicates whether the user is dragging on the target.
        /// </summary>
        public bool IsDragging
        {
            get { return _isDragging; }
        }

        private Point _draggingAnchor = new Point();
        /// <summary>
        /// Gets the starting point of the dragging action. The point is based on the screen.
        /// </summary>
        public virtual Point DraggingAnchor
        {
            get { return _draggingAnchor; }
            protected set { _draggingAnchor = value; }
        }

        private Size _lastMovedVector = new Size();
        /// <summary>
        /// Gets the vector of the last move. The vector is based on the screen.
        /// </summary>
        public virtual Size LastMovedVector
        {
            get { return _lastMovedVector; }
            protected set { _lastMovedVector = value; }
        }

        private Point _location = new Point();
        /// <summary>
        /// Gets the location of the mouse during the generating mouse event.
        /// </summary>
        public virtual Point Location
        {
            get { return _location; }
            protected set { _location = value; }
        }

        /// <summary>
        /// Gets a <see cref="System.Drawing.Size"/> that indicates the dragged size.
        /// </summary>
        public Size DraggedSize
        {
            get
            {
                return new Size(
                    this.Location.X - this._draggingAnchor.X,
                    this.Location.Y - this._draggingAnchor.Y);
            }
        }

        /// <summary>
        /// Gets a <see cref="System.Drawing.Rectangle"/> that indicates the dragged area.
        /// </summary>
        public Rectangle DraggedBounds
        {
            get
            {
                int x1 = Location.X;
                int x2 = this._draggingAnchor.X;
                int y1 = Location.Y;
                int y2 = this._draggingAnchor.Y;

                return new Rectangle(
                    Math.Min(x1, x2),
                    Math.Min(y1, y2),
                    Math.Abs(x2 - x1),
                    Math.Abs(y2 - y1));
            }
        }

        /// <summary>
        /// Refreshes the image coordinations.
        /// </summary>
        public void ContinousDraggingMove()
        {
            Location = _location;
            LastMovedVector = _lastMovedVector;

            if (_lastEventArgs != null)
            {
                AdvancedMouseEventArgs e = CreateAdvancedMouseEventArgs(_lastEventArgs);

                OnMouseMove(e);

                if (_isDragging)
                {
                    OnMouseDragging(e);
                }
                else
                {
                    OnMouseMoveWithoutDragging(e);
                }
            }
        }

        private MouseEventArgs _lastEventArgs = null;

        private AdvancedMouseEventArgs CreateAdvancedMouseEventArgs(
            MouseEventArgs originalEventArgs)
        {
            AdvancedMouseEventArgs e = GetPackagedMouseEventArgs(originalEventArgs);
            _lastEventArgs = originalEventArgs;

            return e;
        }

        /// <summary>
        /// Gets the packaged <see cref="AdvancedMouseEventArgs"/> by original <see cref="MouseEventArgs"/>.
        /// </summary>
        /// <param name="originalEventArgs">A <see cref="MouseEventArgs"/> that contains original event data.</param>
        /// <returns>The packaged <see cref="AdvancedMouseEventArgs"/>.</returns>
        protected virtual AdvancedMouseEventArgs GetPackagedMouseEventArgs(
            MouseEventArgs originalEventArgs)
        {
            return new AdvancedMouseEventArgs(
                originalEventArgs.Button,
                originalEventArgs.Clicks,
                originalEventArgs.X,
                originalEventArgs.Y,
                originalEventArgs.Delta,
                _isDragging,
                _draggingAnchor,
                _lastMovedVector
            );
        }

        #region Event Handlers

        private void control_Click(object sender, EventArgs e)
        {
            MouseEventArgs mouseEventArgs = e as MouseEventArgs;

            switch (mouseEventArgs.Button)
            {
                case MouseButtons.Left:
                    OnMouseLeftButtonClick(CreateAdvancedMouseEventArgs(mouseEventArgs));
                    break;

                case MouseButtons.Right:
                    OnMouseRightButtonClick(CreateAdvancedMouseEventArgs(mouseEventArgs));
                    break;

                default:
                    break;
            }

            OnMouseClick(CreateAdvancedMouseEventArgs(mouseEventArgs));
        }

        private void control_MouseDown(object sender, MouseEventArgs e)
        {
            this.DraggingAnchor = e.Location;
            this.LastMovedVector = new Size();
            this.Location = _draggingAnchor;

            switch (e.Button)
            {
                case MouseButtons.Left:
                    OnMouseLeftButtonDown(CreateAdvancedMouseEventArgs(e));
                    break;

                case MouseButtons.Right:
                    OnMouseRightButtonDown(CreateAdvancedMouseEventArgs(e));
                    break;

                default:
                    break;
            }

            OnMouseDown(CreateAdvancedMouseEventArgs(e));
        }

        private void control_MouseMove(object sender, MouseEventArgs e)
        {
            this.LastMovedVector =
                new Size(e.Location.X - _location.X, e.Location.Y - _location.Y);
            this.Location = e.Location;

            OnMouseMove(CreateAdvancedMouseEventArgs(e));

            if (e.Button != MouseButtons.None)
            {
                if (_isDragging == false)
                {
                    _isDragging = true;

                    OnMouseStartDragging(CreateAdvancedMouseEventArgs(e));
                }

                OnMouseDragging(CreateAdvancedMouseEventArgs(e));
            }
            else
            {
                OnMouseMoveWithoutDragging(CreateAdvancedMouseEventArgs(e));
            }
        }

        private void control_MouseUp(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Left:
                    OnMouseLeftButtonUp(CreateAdvancedMouseEventArgs(e));
                    break;

                case MouseButtons.Right:
                    OnMouseRightButtonUp(CreateAdvancedMouseEventArgs(e));
                    break;

                default:
                    break;
            }

            OnMouseUp(CreateAdvancedMouseEventArgs(e));

            if (_isDragging)
            {
                // Drag Done
                _isDragging = false;

                OnMouseDraggingFinished(CreateAdvancedMouseEventArgs(e));
            }

        }

        #endregion

        #region Private Members

        private Control _control = null;

        private void Register(Control control)
        {
            if (control == null)
            {
                throw new ArgumentNullException("control");
            }

            _control = control;

            _control.Click += control_Click;
            _control.MouseDown += control_MouseDown;
            _control.MouseMove += control_MouseMove;
            _control.MouseUp += control_MouseUp;
        }

        private void Unregister()
        {
            _control.Click -= control_Click;
            _control.MouseDown -= control_MouseDown;
            _control.MouseMove -= control_MouseMove;
            _control.MouseUp -= control_MouseUp;

            _control = null;
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when the attached control is clicked by the mouse.
        /// </summary>
        public event AdvancedMouseEventHandler MouseClick;
        /// <summary>
        /// Raises the <see cref="MouseClick"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseClick(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseClick;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the attached control and a mouse button is pressed.
        /// </summary>
        public event AdvancedMouseEventHandler MouseDown;
        /// <summary>
        /// Raises the <see cref="MouseDown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseDown(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseDown;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the attached control and a mouse button is released.
        /// </summary>
        public event AdvancedMouseEventHandler MouseUp;
        /// <summary>
        /// Raises the <see cref="MouseUp"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseUp(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseUp;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the attached control is left-clicked by the mouse.
        /// </summary>
        public event AdvancedMouseEventHandler MouseLeftButtonClick;
        /// <summary>
        /// Raises the <see cref="MouseLeftButtonClick"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseLeftButtonClick(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseLeftButtonClick;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the attached control is right-clicked by the mouse.
        /// </summary>
        public event AdvancedMouseEventHandler MouseRightButtonClick;
        /// <summary>
        /// Raises the <see cref="MouseRightButtonClick"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseRightButtonClick(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseRightButtonClick;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the attached control and the mouse left button is pressed.
        /// </summary>
        public event AdvancedMouseEventHandler MouseLeftButtonDown;
        /// <summary>
        /// Raises the <see cref="MouseLeftButtonDown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseLeftButtonDown(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseLeftButtonDown;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the attached control and the mouse left button is released.
        /// </summary>
        public event AdvancedMouseEventHandler MouseLeftButtonUp;
        /// <summary>
        /// Raises the <see cref="MouseLeftButtonUp"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseLeftButtonUp(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseLeftButtonUp;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the attached control and the mouse right button is pressed.
        /// </summary>
        public event AdvancedMouseEventHandler MouseRightButtonDown;
        /// <summary>
        /// Raises the <see cref="MouseRightButtonDown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseRightButtonDown(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseRightButtonDown;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the attached control and the mouse right button is released.
        /// </summary>
        public event AdvancedMouseEventHandler MouseRightButtonUp;
        /// <summary>
        /// Raises the <see cref="MouseRightButtonUp"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseRightButtonUp(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseRightButtonUp;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is moved over the attached control.
        /// </summary>
        public event AdvancedMouseEventHandler MouseMove;
        /// <summary>
        /// Raises the <see cref="MouseMove"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseMove(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseMove;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is moved over the attached control with no mouse button pressed.
        /// </summary>
        public event AdvancedMouseEventHandler MouseMoveWithoutDragging;
        /// <summary>
        /// Raises the <see cref="MouseMoveWithoutDragging"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseMoveWithoutDragging(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseMoveWithoutDragging;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse just starts to move over the attached control with a mouse button pressed.
        /// </summary>
        public event AdvancedMouseEventHandler MouseStartDragging;
        /// <summary>
        /// Raises the <see cref="MouseStartDragging"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseStartDragging(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseStartDragging;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse pointer is moved over the attached control with mouse button is pressed.
        /// </summary>
        public event AdvancedMouseEventHandler MouseDragging;
        /// <summary>
        /// Raises the <see cref="MouseDragging"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseDragging(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseDragging;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when the mouse button is just released when moving over the attached control.
        /// </summary>
        public event AdvancedMouseEventHandler MouseDraggingFinished;
        /// <summary>
        /// Raises the <see cref="MouseDraggingFinished"/> event.
        /// </summary>
        /// <param name="e">A <see cref="AdvancedMouseEventArgs"/> that contains the event data.</param>
        protected virtual void OnMouseDraggingFinished(AdvancedMouseEventArgs e)
        {
            AdvancedMouseEventHandler handler = MouseDraggingFinished;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by <see cref="AdvancedMouseEventsMonitor"/> class.
        /// </summary>
        public void Dispose()
        {
            Unregister();
        }

        #endregion
    }
}
